package msu.ml.tools;

import msu.ml.data.*;
import msu.ml.data.level2.*;
import msu.ml.classification.*;
import msu.ml.util.*;
import weka.core.*;
import weka.classifiers.*;
import weka.classifiers.bayes.*;
import weka.classifiers.functions.*;
import weka.classifiers.evaluation.*;
import java.util.*;

public class SimpleGooseExperiment
{
   public static void main(String [] args)
   {
      String syntax = "{c$classifier} {h$helpme}";
      CommandLineParser parser = null;

      try
      {
         parser = new CommandLineParser(syntax);
         parser.parse(args);
      }
      catch(CommandLineSyntaxParseException e)
      {
         System.out.println(e.getMessage());
         System.exit(0);
      }
      catch(CommandLineParseException e)
      {
         System.out.println(e.getMessage());
         System.exit(0);
      }

      if(parser.hasOption("helpme"))
      {
         System.out.println("SimpleGooseExperiment [OPTIONS]");
         System.out.println("   -h,--help          Print this help screen");
         System.out.println("   -c,--classifier    Classifier to use, naive_bayes or neural_net(DEFAULT)");
         System.out.println();
         System.exit(0);
      }

      DatabaseModel database = new DatabaseModel("jdbc:mysql://localhost/geese", "rmead", "");

      DynamicInstances data = new DynamicInstances(
            database.getBiologicalInstances());
      data.concatenate(database.getNonBiologicalInstances());
      data.setClassIndex(data.numAttributes() - 1);

      Classifier classifier = null;

      if(parser.hasOption("classifier") && parser.getOption("classifier").value.equals("naive_bayes"))
      {
         classifier = new NaiveBayes();
      }
      else
      {
         MultilayerPerceptron mp = new MultilayerPerceptron();
         mp.setAutoBuild(true);
         mp.setLearningRate(.3);
         mp.setDecay(false);
         mp.setHiddenLayers("3");
         mp.setTrainingTime(300);
         mp.setNormalizeAttributes(true);
         mp.setValidationSetSize(5);
         classifier = mp;
      }

      balanceData(data);

      try
      {
         int folds = 10;

         Instances rand = new Instances(data);
         rand.setClassIndex(data.numAttributes() - 1);
         rand.randomize(new Random((int)System.currentTimeMillis()));
         rand.stratify(folds);

         ConfusionMatrix matrix = new ConfusionMatrix(new String [] {
               "non", "bio" });

         for(int i = 0; i < folds; i++)
         {
            Instances train = rand.trainCV(folds, i);
            Instances test = rand.testCV(folds, i);
            test.setClassIndex(test.numAttributes() - 1);

            classifier.buildClassifier(train);

            int dist [][] = new int[2][2];

            for(int j = 0; j < test.numInstances(); j++)
            {
               Instance inst = test.instance(j);

               matrix.addPrediction(new NominalPrediction(inst.classValue(),
                        classifier.distributionForInstance(inst)));
            }
         }

         System.out.println(matrix);
         System.out.println(String.format("Accuracy = %1$.2f", 
                  (matrix.correct() / matrix.total())));

      }
      catch(Exception e)
      {
         e.printStackTrace();
      }
   }

   private static void balanceData(Instances data)
   {
      //RUS & ROS here
      int [] dist = new int[2];
      for(int i = 0; i < data.numInstances(); i++)
         dist[(int)(data.instance(i).classValue())]++;

      System.out.println(String.format("%1$d Class 0 Training Examples", dist[0]));
      System.out.println(String.format("%1$d Class 1 Training Examples", dist[1]));
      System.out.println("Performing RUS and ROS");

      int max = (dist[1] > dist[0]) ? 1 : 0;
      int min = (max == 1) ? 0 : 1;

      int diff1 = dist[max] - ((dist[max] + dist[min]) / 2);
      int diff2 = ((dist[max] + dist[min]) / 2) - dist[min];

      while(!(diff1 == 0 && diff2 == 0))
      {
         if((diff1 + diff2) % 10000 == 0)
            System.out.print(".");
         int index = (int)(Math.random() * (double)data.numInstances());
         if(diff1 != 0 && max == (int)(data.instance(index).classValue()))
         {
            data.delete(index);
            diff1--;
         }
         else if(diff2 != 0 && min == (int)(data.instance(index).classValue()))
         {
            data.add((Instance)(data.instance(index).copy()));
            diff2--;
         }
      }
      System.out.println();

      dist = new int[2];
      for(int i = 0; i < data.numInstances(); i++)
         dist[(int)(data.instance(i).classValue())]++;

      System.out.println(String.format("%1$d Class 0 Training Examples", dist[0]));
      System.out.println(String.format("%1$d Class 1 Training Examples", dist[1]));

   }
}
